package language.advanced.generic;

import org.junit.Test;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.function.Consumer;

public class DuckType {

    //反射模拟鸭子类型 `invoke()` 和 `apply()` 的优点是它们可以接受任意数量的参数
    public static <T, S extends Iterable<T>> void apply(S seq, String f, Object... args) throws Exception {
        for(T t: seq){
            Class<?> spkr = t.getClass();
            Method method = spkr.getMethod(f);
            method.invoke(t, args);
        }
    }

    @Test
    public void duckType() throws Exception {
        //shape和square没有任何关系，可以通过反射执行同一个方法rotate
        ArrayList<Object> seqs = new ArrayList<>();
        seqs.add(new Shape());
        seqs.add(new Shape());
        seqs.add(new Square());
        DuckType.apply(seqs, "rotate");
    }

    //用方法引用实现鸭子类型
    //在调用Consumer的accept()方法时，会将该方法的引用绑定到具体的执行对象，再调用该方法
    //必须显式地为 `perform()` 提供要使用的方法引用。 它不用只按名称调用方法。更通用
    public static <P> void perform(P performer, Consumer<P> action1, Consumer<P> action2) {
        action1.accept(performer);
        action2.accept(performer);
    }

    @Test //也可以使用方法引用来完成
    public void function() throws Exception {
        DuckType.perform(new Shape(), Shape::rotate, Shape::toString);
        DuckType.perform(new Square(), Square::rotate, Square::toString);
    }
}


class Shape {
    private static long counter = 0;
    private final long id = counter++;
    @Override
    public String toString() {
        return getClass().getSimpleName() + " " + id;
    }
    public void rotate() {
        System.out.println(this + " rotate");
    }
    public void resize(int newSize) {
        System.out.println(this + " resize " + newSize);
    }
}
class Square {
    public void rotate() {
        System.out.println("square" + " rotate");
    }
}